package com.klod.news.data

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.HttpTimeout
import io.ktor.client.request.prepareGet
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode
import io.ktor.utils.io.ByteReadChannel
import io.ktor.utils.io.jvm.javaio.toInputStream
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import org.koin.compose.koinInject
import java.io.File
import java.io.FileOutputStream
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.math.roundToInt

/**
 * 下载状态
 */
sealed class DownloadState {
    object Idle : DownloadState()
    data class Progress(val progress: Int) : DownloadState()
    object Paused : DownloadState()
    object Completed : DownloadState()
    data class Error(val throwable: Throwable) : DownloadState()
}

/**
 * 下载任务
 */
data class DownloadTask(
    val url: String,
    val file: File,
    var downloadState: DownloadState = DownloadState.Idle
)

/**
 * 下载框架
 */
class Downloader(
    private val client: HttpClient  = KtorClient.client,
    private val scope: CoroutineScope = CoroutineScope(Dispatchers.IO)
) {

    private val downloadQueue = ArrayDeque<DownloadTask>()
    private var currentDownloadJob: Job? = null
    private val isPaused = AtomicBoolean(false)

    private val _downloadStateFlow = MutableSharedFlow<Pair<DownloadTask, DownloadState>>()
    val downloadStateFlow: Flow<Pair<DownloadTask, DownloadState>> = _downloadStateFlow.asSharedFlow()

    /**
     * 添加下载任务
     * @param url 下载链接
     * @param file 保存文件
     */
    fun addDownloadTask(url: String, file: File) {
        val task = DownloadTask(url, file)
        downloadQueue.add(task)
        scope.launch {
            _downloadStateFlow.emit(Pair(task, DownloadState.Idle))
        }
        startNextDownload()
    }

    /**
     * 暂停下载
     */
    fun pauseDownload() {
        isPaused.set(true)
        currentDownloadJob?.cancel()
    }

    /**
     * 继续下载
     */
    fun resumeDownload() {
        isPaused.set(false)
        startNextDownload()
    }

    /**
     * 取消下载
     */
    fun cancelDownload() {
        downloadQueue.clear()
        currentDownloadJob?.cancel()
    }

    /**
     * 开始下一个下载任务
     */
    private fun startNextDownload() {
        if (currentDownloadJob?.isActive == true) {
            return
        }
        if (downloadQueue.isEmpty()) {
            return
        }
        val task = downloadQueue.removeFirst()
        currentDownloadJob = scope.launch {
            try {
                download(task)
            } catch (e: Exception) {
                task.downloadState = DownloadState.Error(e)
                _downloadStateFlow.emit(Pair(task, task.downloadState))
            } finally {
                startNextDownload()
            }
        }
    }

    /**
     * 下载
     * @param task 下载任务
     */
    private suspend fun download(task: DownloadTask) {
        val downloadedBytes = task.file.length()
        val response = client.prepareGet(task.url) {
            //断点续传
            if (downloadedBytes > 0) {
                headers.append(HttpHeaders.Range, "bytes=$downloadedBytes-")
            }
        }.execute()
        if (response.status == HttpStatusCode.PartialContent || response.status == HttpStatusCode.OK) {
            val totalBytes = response.headers[HttpHeaders.ContentRange]?.substringAfterLast("/")?.toLongOrNull()
                ?: response.headers[HttpHeaders.ContentLength]?.toLongOrNull()
                ?: 0L
            val channel: ByteReadChannel = response.body()
            val inputStream = channel.toInputStream()
            FileOutputStream(task.file, true).use { outputStream ->
                val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
                var bytesRead: Int
                var currentBytes = downloadedBytes
                while (inputStream.read(buffer).also { bytesRead = it } != -1 && scope.isActive) {
                    if (isPaused.get()) {
                        task.downloadState = DownloadState.Paused
                        _downloadStateFlow.emit(Pair(task, task.downloadState))
                        while (isPaused.get()) {
                            //等待继续
                        }
                    }
                    outputStream.write(buffer, 0, bytesRead)
                    currentBytes += bytesRead
                    val progress = ((currentBytes.toDouble() / totalBytes) * 100).roundToInt()
                    task.downloadState = DownloadState.Progress(progress)
                    _downloadStateFlow.emit(Pair(task, task.downloadState))
                }
            }
            task.downloadState = DownloadState.Completed
            _downloadStateFlow.emit(Pair(task, task.downloadState))
        } else {
            task.downloadState = DownloadState.Error(Throwable("下载失败,状态码:${response.status}"))
            _downloadStateFlow.emit(Pair(task, task.downloadState))
        }
    }
}

@Composable
fun DownloadExample() {
    val context = LocalContext.current
    val downloader = remember { Downloader() }
    var downloadState by remember { mutableStateOf<DownloadState>(DownloadState.Idle) }
    var downloadState2 by remember { mutableStateOf<DownloadState>(DownloadState.Idle) }
    var downloadState3 by remember { mutableStateOf<DownloadState>(DownloadState.Idle) }

    LaunchedEffect(key1 = downloader) {
        downloader.downloadStateFlow.collectLatest { (task, state) ->
            when (task.file.name) {
                "file1.apk" -> downloadState = state
                "file2.apk" -> downloadState2 = state
                "file3.apk" -> downloadState3 = state
            }
        }
    }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(text = "Download State 1: $downloadState")
        Button(onClick = {
            val url = "https://down.qq.com/qqweb/QQ_1/android_apk/Android_8.9.88_5388.apk"
            val file = File(context.getExternalFilesDir(null), "file1.apk")
            downloader.addDownloadTask(url, file)
        }) {
            Text("Start Download 1")
        }
        Text(text = "Download State 2: $downloadState2")
        Button(onClick = {
            val url = "https://down.qq.com/qqweb/QQ_1/android_apk/Android_8.9.88_5388.apk"
            val file = File(context.getExternalFilesDir(null), "file2.apk")
            downloader.addDownloadTask(url, file)
        }) {
            Text("Start Download 2")
        }
        Text(text = "Download State 3: $downloadState3")
        Button(onClick = {
            val url = "https://down.qq.com/qqweb/QQ_1/android_apk/Android_8.9.88_5388.apk"
            val file = File(context.getExternalFilesDir(null), "file3.apk")
            downloader.addDownloadTask(url, file)
        }) {
            Text("Start Download 3")
        }
        Button(onClick = {
            downloader.pauseDownload()
        }) {
            Text("Pause Download")
        }
        Button(onClick = {
            downloader.resumeDownload()
        }) {
            Text("Resume Download")
        }
        Button(onClick = {
            downloader.cancelDownload()
        }) {
            Text("Cancel Download")
        }
    }
}